/*
 * Created on 2004-9-20
 */
package org.nanhill.commons.lang;

import static org.nanhill.commons.lang.StringConstants.EMPTY;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * String utility class.
 * 
 * Noninstantiable class, all methods are null safe and thread safe.
 * 
 * @author xalinx at gmail dot com
 * @version 2006-12-28
 */
public final class StringKit {
	/**
	 * 解析出下列语法所构成的<code>SENTENCE</code>。
	 * 
	 * <pre>
	 *  SENTENCE = WORD (DELIMITER* WORD)*
	 * 
	 *  WORD = UPPER_CASE_WORD | LOWER_CASE_WORD | TITLE_CASE_WORD | DIGIT_WORD
	 * 
	 *  UPPER_CASE_WORD = UPPER_CASE_LETTER+
	 *  LOWER_CASE_WORD = LOWER_CASE_LETTER+
	 *  TITLE_CASE_WORD = UPPER_CASE_LETTER LOWER_CASE_LETTER+
	 *  DIGIT_WORD      = DIGIT+
	 * 
	 *  UPPER_CASE_LETTER = Character.isUpperCase()
	 *  LOWER_CASE_LETTER = Character.isLowerCase()
	 *  DIGIT             = Character.isDigit()
	 *  NON_LETTER_DIGIT  = !Character.isUpperCase() &amp;&amp; !Character.isLowerCase() &amp;&amp; !Character.isDigit()
	 * 
	 *  DELIMITER = WHITESPACE | NON_LETTER_DIGIT
	 * </pre>
	 */
	private abstract static class WordTokenizer {
		protected static final char UNDERSCORE = '_';

		/**
		 * Parse sentence。
		 */
		public String parse(String str) {
			if (isEmpty(str)) {
				return str;
			}

			int length = str.length();
			StringBuffer buffer = new StringBuffer(length);

			for (int index = 0; index < length; index++) {
				char ch = str.charAt(index);

				// 忽略空白。
				if (Character.isWhitespace(ch)) {
					continue;
				}

				// 大写字母开始：UpperCaseWord或是TitleCaseWord。
				if (Character.isUpperCase(ch)) {
					int wordIndex = index + 1;

					while (wordIndex < length) {
						char wordChar = str.charAt(wordIndex);

						if (Character.isUpperCase(wordChar)) {
							wordIndex++;
						} else if (Character.isLowerCase(wordChar)) {
							wordIndex--;
							break;
						} else {
							break;
						}
					}

					// 1. wordIndex == length，说明最后一个字母为大写，以upperCaseWord处理之。
					// 2. wordIndex == index，说明index处为一个titleCaseWord。
					// 3. wordIndex > index，说明index到wordIndex -
					// 1处全部是大写，以upperCaseWord处理。
					if ((wordIndex == length) || (wordIndex > index)) {
						index = parseUpperCaseWord(buffer, str, index,
								wordIndex);
					} else {
						index = parseTitleCaseWord(buffer, str, index);
					}

					continue;
				}

				// 小写字母开始：LowerCaseWord。
				if (Character.isLowerCase(ch)) {
					index = parseLowerCaseWord(buffer, str, index);
					continue;
				}

				// 数字开始：DigitWord。
				if (Character.isDigit(ch)) {
					index = parseDigitWord(buffer, str, index);
					continue;
				}

				// 非字母数字开始：Delimiter。
				inDelimiter(buffer, ch);
			}

			return buffer.toString();
		}

		private int parseUpperCaseWord(StringBuffer buffer, String str,
				int index, int length) {
			char ch = str.charAt(index++);

			// 首字母，必然存在且为大写。
			if (buffer.length() == 0) {
				startSentence(buffer, ch);
			} else {
				startWord(buffer, ch);
			}

			// 后续字母，必为小写。
			for (; index < length; index++) {
				ch = str.charAt(index);
				inWord(buffer, ch);
			}

			return index - 1;
		}

		private int parseLowerCaseWord(StringBuffer buffer, String str,
				int index) {
			char ch = str.charAt(index++);

			// 首字母，必然存在且为小写。
			if (buffer.length() == 0) {
				startSentence(buffer, ch);
			} else {
				startWord(buffer, ch);
			}

			// 后续字母，必为小写。
			int length = str.length();

			for (; index < length; index++) {
				ch = str.charAt(index);

				if (Character.isLowerCase(ch)) {
					inWord(buffer, ch);
				} else {
					break;
				}
			}

			return index - 1;
		}

		private int parseTitleCaseWord(StringBuffer buffer, String str,
				int index) {
			char ch = str.charAt(index++);

			// 首字母，必然存在且为大写。
			if (buffer.length() == 0) {
				startSentence(buffer, ch);
			} else {
				startWord(buffer, ch);
			}

			// 后续字母，必为小写。
			int length = str.length();

			for (; index < length; index++) {
				ch = str.charAt(index);

				if (Character.isLowerCase(ch)) {
					inWord(buffer, ch);
				} else {
					break;
				}
			}

			return index - 1;
		}

		private int parseDigitWord(StringBuffer buffer, String str, int index) {
			char ch = str.charAt(index++);

			// 首字符，必然存在且为数字。
			if (buffer.length() == 0) {
				startDigitSentence(buffer, ch);
			} else {
				startDigitWord(buffer, ch);
			}

			// 后续字符，必为数字。
			int length = str.length();

			for (; index < length; index++) {
				ch = str.charAt(index);

				if (Character.isDigit(ch)) {
					inDigitWord(buffer, ch);
				} else {
					break;
				}
			}

			return index - 1;
		}

		protected boolean isDelimiter(char ch) {
			return !Character.isUpperCase(ch) && !Character.isLowerCase(ch)
					&& !Character.isDigit(ch);
		}

		protected abstract void startSentence(StringBuffer buffer, char ch);

		protected abstract void startWord(StringBuffer buffer, char ch);

		protected abstract void inWord(StringBuffer buffer, char ch);

		protected abstract void startDigitSentence(StringBuffer buffer, char ch);

		protected abstract void startDigitWord(StringBuffer buffer, char ch);

		protected abstract void inDigitWord(StringBuffer buffer, char ch);

		protected abstract void inDelimiter(StringBuffer buffer, char ch);
	}

	/** 解析单词的解析器。 */
	private static final WordTokenizer CAMEL_CASE_TOKENIZER = new WordTokenizer() {
		protected void startSentence(StringBuffer buffer, char ch) {
			buffer.append(Character.toLowerCase(ch));
		}

		protected void startWord(StringBuffer buffer, char ch) {
			if (!isDelimiter(buffer.charAt(buffer.length() - 1))) {
				buffer.append(Character.toUpperCase(ch));
			} else {
				buffer.append(Character.toLowerCase(ch));
			}
		}

		protected void inWord(StringBuffer buffer, char ch) {
			buffer.append(Character.toLowerCase(ch));
		}

		protected void startDigitSentence(StringBuffer buffer, char ch) {
			buffer.append(ch);
		}

		protected void startDigitWord(StringBuffer buffer, char ch) {
			buffer.append(ch);
		}

		protected void inDigitWord(StringBuffer buffer, char ch) {
			buffer.append(ch);
		}

		protected void inDelimiter(StringBuffer buffer, char ch) {
			if (ch != UNDERSCORE) {
				buffer.append(ch);
			}
		}
	};

	/**
	 * Suppress default constructor for noninstantiability.
	 */
	private StringKit() {
	}

	/**
	 * Checks if a String is empty ("") or null.
	 * 
	 * <p>
	 * Examples: <code>
	 *      isEmpty(null)= true
	 *      isEmpty("")= true
	 *      isEmpty(" ") = false
	 *      isEmpty("alin")= false
	 * </code>
	 * </p>
	 * 
	 * @param str
	 *            the String to check, may be null
	 * @return <code>true</code> if the String is empty or null
	 */
	public static boolean isEmpty(String str) {
		return str == null || str.length() == 0;
	}

	/**
	 * Checks if a String is is empty ("") or null or trim to empty("").
	 * 
	 * <p>
	 * Examples: <code>
	 *      isEmpty(null)= true
	 *      isEmpty("")= true
	 *      isEmpty(" ") = true
	 *      isEmpty("alin")= false
	 * </code>
	 * </p>
	 * 
	 * @param str
	 *            the String to check, may be null
	 * @return
	 */
	public static boolean isBlank(String str) {
		int length;

		if ((str == null) || ((length = str.length()) == 0)) {
			return true;
		}

		for (int i = 0; i < length; i++) {
			if (!Character.isWhitespace(str.charAt(i))) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Checks if two String are all null or <code>des.equals(src)</code>
	 * 
	 * @param des
	 * @param src
	 * @return return true if des and src are all null or
	 *         <code>des.equals(src)</code>, else false.
	 */
	public static boolean equals(String des, String src) {
		if (null == des) {
			return null == src;
		} else {
			return des.equals(src);
		}
	}

	public static String trimToNull(String in) {
		if (null == in) {
			return null;
		}
		String out = in.trim();
		if (0 == out.length()) {
			return null;
		} else {
			return out;
		}
	}

	/**
	 * Null safe
	 * 
	 * null,"" --> null; else do nothing
	 * 
	 * @param in
	 * @return
	 */
	public static String emptyToNull(String in) {
		if (null != in && 0 == in.length()) {
			return null;
		} else {
			return in;
		}
	}

	public static String nullToEmpty(String in) {
		if (null == in) {
			return EMPTY;
		} else {
			return in;
		}
	}

	/**
	 * Null safe
	 * 
	 * null --> "", else return String.trim()
	 * 
	 * @return "" if the input is null or return in.trim()
	 * @since 1.0
	 */
	public static String trimToEmpty(String in) {
		if (null == in) {
			return EMPTY;
		} else {
			return in.trim();
		}
	}

	public static boolean stringToBooleanTrueFirst(String in) {
		return in.equalsIgnoreCase("true") || in.equalsIgnoreCase("yes") ? true
				: false;
	}

	public static String strip(String str, String stripChars) {
		if (str == null || str.length() == 0) {
			return str;
		}
		str = stripStart(str, stripChars);
		return stripEnd(str, stripChars);
	}

	public static String stripStart(String str, String stripChars) {
		int strLen;
		if (str == null || (strLen = str.length()) == 0) {
			return str;
		}
		int start = 0;
		if (stripChars == null) {
			while ((start != strLen)
					&& Character.isWhitespace(str.charAt(start))) {
				start++;
			}
		} else if (stripChars.length() == 0) {
			return str;
		} else {
			while ((start != strLen)
					&& (stripChars.indexOf(str.charAt(start)) != -1)) {
				start++;
			}
		}
		return str.substring(start);
	}

	public static String stripEnd(String str, String stripChars) {
		int end;
		if (str == null || (end = str.length()) == 0) {
			return str;
		}

		if (stripChars == null) {
			while ((end != 0) && Character.isWhitespace(str.charAt(end - 1))) {
				end--;
			}
		} else if (stripChars.length() == 0) {
			return str;
		} else {
			while ((end != 0)
					&& (stripChars.indexOf(str.charAt(end - 1)) != -1)) {
				end--;
			}
		}
		return str.substring(0, end);
	}

	/**
	 * Strip all characters out of <var>s </var> that could not be part of a
	 * valid Java identifier. Return either the given string (if all characters
	 * were valid), or a new string with all invalid characters stripped out.
	 */
	public static String stripNotValidIdentifier(String s) {
		int len = s.length();
		int pos = 0;
		for (int i = 0; i < len; i++, pos++) {
			if (!Character.isJavaIdentifierPart(s.charAt(i)))
				break;
		}
		if (pos == len)
			return s;

		StringBuffer buf = new StringBuffer(len);
		for (int i = 0; i < pos; i++)
			buf.append(s.charAt(i));

		// skip pos, we know it's not a valid char from above
		for (int i = pos + 1; i < len; i++) {
			char c = s.charAt(i);
			if (Character.isJavaIdentifierPart(c))
				buf.append(c);
		}
		return buf.toString();
	}

	/**
	 * 将字符串转换成camel case。
	 * 
	 * <p>
	 * 如果字符串是<code>null</code>则返回<code>null</code>。
	 * 
	 * <pre>
	 * StringUtil.toCamelCase(null)  = null
	 * StringUtil.toCamelCase(&quot;&quot;)    = &quot;&quot;
	 * StringUtil.toCamelCase(&quot;aBc&quot;) = &quot;aBc&quot;
	 * StringUtil.toCamelCase(&quot;aBc def&quot;) = &quot;aBcDef&quot;
	 * StringUtil.toCamelCase(&quot;aBc def_ghi&quot;) = &quot;aBcDefGhi&quot;
	 * StringUtil.toCamelCase(&quot;aBc def_ghi 123&quot;) = &quot;aBcDefGhi123&quot;
	 * </pre>
	 * 
	 * </p>
	 * 
	 * <p>
	 * 此方法会保留除了下划线和空白以外的所有分隔符。
	 * </p>
	 * 
	 * @param str
	 *            要转换的字符串
	 * 
	 * @return camel case字符串，如果原字符串为<code>null</code>，则返回<code>null</code>
	 */
	public static String toCamelCase(String str) {
		return CAMEL_CASE_TOKENIZER.parse(str);
	}

	public static int sum(String in, char searched) {
		if (null == in) {
			return 0;
		}
		int sum = 0;
		char[] inChars = in.toCharArray();
		for (int i = 0; i < inChars.length; i++) {
			if (searched == inChars[i]) {
				sum++;
			}
		}
		return sum;
	}

	public static String replace(String in, char desChar, char refillChar) {
		if (null == in) {
			return null;
		}
		return in.replace(desChar, refillChar);
	}

	/**
	 * Replaces all occurrences characters within replace character.
	 * 
	 * @param src
	 *            input string
	 * @param del
	 *            occurrences characters
	 * @param replace
	 *            replace character
	 * @return
	 */
	public static String replace(final String src, final char[] del,
			final char replace) {
		if (src == null || src.length() == 0 || null == del || del.length == 0) {
			return src;
		}
		int srcLen = src.length();
		StringBuilder buffer = new StringBuilder(srcLen);
		int delLen = del.length;
		outer: for (int i = 0, n = srcLen; i < n; i++) {
			char check = src.charAt(i);
			for (int j = 0; j < delLen; j++) {
				if (check == del[j]) {
					buffer.append(replace);
					continue outer;
				}
			}
			buffer.append(check);
		}
		return buffer.toString();
	}

	public static String replace(final String in, final char search,
			final String replace) {
		if (null == in || null == replace) {
			throw new UnexpectedException(
					"Search or replace charactor expect not null!");
		}
		int inlen = in.length();
		if (0 == inlen) {
			return in;
		}
		char[] val = in.toCharArray();
		int firstloc = -1;
		while (++firstloc < inlen) {
			if (search == val[firstloc]) {
				break;
			}
		}
		// no search charactor
		if (firstloc == inlen)
			return in;
		StringBuilder buf = new StringBuilder(inlen + 24);
		for (int j = 0; j < firstloc; j++) {
			buf.append(val[j]);
		}
		buf.append(replace);
		// log.debug(buf.toString());

		for (int k = firstloc + 1; k < inlen; k++) {
			char temp = val[k];
			if (search == temp) {
				buf.append(replace);
			} else {
				buf.append(temp);
			}
		}
		return buf.toString();
	}

	/**
	 * <p>
	 * Replaces all occurrences of a String within another String.
	 * </p>
	 * 
	 * <p>
	 * A <code>null</code> reference passed to this method is a no-op.
	 * </p>
	 * 
	 * <p>
	 * 
	 * <pre>
	 *    StringUtils.replace(null, *, *)        = null
	 *    StringUtils.replace(&quot;&quot;, *, *)          = &quot;&quot;
	 *    StringUtils.replace(&quot;any&quot;, null, *)    = &quot;any&quot;
	 *    StringUtils.replace(&quot;any&quot;, *, null)    = &quot;any&quot;
	 *    StringUtils.replace(&quot;any&quot;, &quot;&quot;, *)      = &quot;any&quot;
	 *    StringUtils.replace(&quot;aba&quot;, &quot;a&quot;, null)  = &quot;aba&quot;
	 *    StringUtils.replace(&quot;aba&quot;, &quot;a&quot;, &quot;&quot;)    = &quot;b&quot;
	 *    StringUtils.replace(&quot;aba&quot;, &quot;a&quot;, &quot;z&quot;)   = &quot;zbz&quot;
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @see #replace(String text, String repl, String with, int max)
	 * @param text
	 *            text to search and replace in, may be null
	 * @param repl
	 *            the String to search for, may be null
	 * @param with
	 *            the String to replace with, may be null
	 * @return the text with any replacements processed, <code>null</code> if
	 *         null String input
	 */
	public static String replace(String text, String repl, String with) {
		return replace(text, repl, with, -1);
	}

	/**
	 * <p>
	 * Replaces a String with another String inside a larger String, for the
	 * first <code>max</code> values of the search String.
	 * </p>
	 * 
	 * <p>
	 * A <code>null</code> reference passed to this method is a no-op.
	 * </p>
	 * 
	 * <p>
	 * 
	 * <pre>
	 *   StringUtils.replace(null, *, *, *)         = null
	 *   StringUtils.replace(&quot;&quot;, *, *, *)           = &quot;&quot;
	 *   StringUtils.replace(&quot;any&quot;, null, *, *)     = &quot;any&quot;
	 *   StringUtils.replace(&quot;any&quot;, *, null, *)     = &quot;any&quot;
	 *   StringUtils.replace(&quot;any&quot;, &quot;&quot;, *, *)       = &quot;any&quot;
	 *   StringUtils.replace(&quot;any&quot;, *, *, 0)        = &quot;any&quot;
	 *   StringUtils.replace(&quot;abaa&quot;, &quot;a&quot;, null, -1) = &quot;abaa&quot;
	 *   StringUtils.replace(&quot;abaa&quot;, &quot;a&quot;, &quot;&quot;, -1)   = &quot;b&quot;
	 *   StringUtils.replace(&quot;abaa&quot;, &quot;a&quot;, &quot;z&quot;, 0)   = &quot;abaa&quot;
	 *   StringUtils.replace(&quot;abaa&quot;, &quot;a&quot;, &quot;z&quot;, 1)   = &quot;zbaa&quot;
	 *   StringUtils.replace(&quot;abaa&quot;, &quot;a&quot;, &quot;z&quot;, 2)   = &quot;zbza&quot;
	 *   StringUtils.replace(&quot;abaa&quot;, &quot;a&quot;, &quot;z&quot;, -1)  = &quot;zbzz&quot;
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param text
	 *            text to search and replace in, may be null
	 * @param repl
	 *            the String to search for, may be null
	 * @param with
	 *            the String to replace with, may be null
	 * @param max
	 *            maximum number of values to replace, or <code>-1</code> if
	 *            no maximum
	 * @return the text with any replacements processed, <code>null</code> if
	 *         null String input
	 */
	public static String replace(String text, String repl, String with, int max) {
		if (text == null || isEmpty(repl) || with == null || max == 0) {
			return text;
		}
		StringBuilder buf = new StringBuilder(text.length());
		int start = 0, end = 0;
		while ((end = text.indexOf(repl, start)) != -1) {
			buf.append(text.substring(start, end)).append(with);
			start = end + repl.length();
			if (--max == 0) {
				break;
			}
		}
		buf.append(text.substring(start));
		return buf.toString();
	}

	/**
	 * replace
	 * 
	 * @param str
	 * @param searchChars
	 * @param replaceChars
	 * @return
	 */
	/*
	 * public static String replaceChars(String str, String searchChars, String
	 * replaceChars) { if (str == null || str.length() == 0 || searchChars ==
	 * null || searchChars.length() == 0) { return str; } char[] chars =
	 * str.toCharArray(); int len = chars.length; boolean modified = false; for
	 * (int i = 0, isize = searchChars.length(); i < isize; i++) { char
	 * searchChar = searchChars.charAt(i); if (replaceChars == null || i >=
	 * replaceChars.length()) { // delete int pos = 0; for (int j = 0; j < len;
	 * j++) { if (chars[j] != searchChar) { chars[pos++] = chars[j]; } else {
	 * modified = true; } } len = pos; } else { // replace for (int j = 0; j <
	 * len; j++) { if (chars[j] == searchChar) { chars[j] =
	 * replaceChars.charAt(i); modified = true; } } } } if (modified == false) {
	 * return str; } return new String(chars, 0, len); }
	 */

	/**
	 * replace the key of input Map in the input string with the corresponding
	 * value.
	 * 
	 * 
	 * @param src
	 * @param map
	 * @return
	 */
	// public static String replaceKeyWithValue(String src, Map<String, String>
	// map) {
	// if (src == null || src.length() == 0 || null == map || 0 == map.size()) {
	// return src;
	// }
	// }
	/**
	 * 
	 * @param str
	 *            input string
	 * @param charactor
	 *            find char
	 * @return null if str is null, length 0, or doesn't contains any spliter
	 *         charactor. else return the char locations array.
	 */
	public static int[] getCharLocations(String str, char charactor) {
		if (str == null) {
			return null;
		}
		int len = str.length();
		if (0 == len) {
			return null;
		}
		List<Integer> list = new ArrayList<Integer>();
		for (int i = 0; i < len; i++) {
			if (str.charAt(i) == charactor) {
				list.add(i);
			}
		}
		int spliterSize = list.size();
		if (0 == spliterSize) {
			return null;
		}
		int[] result = new int[spliterSize];
		int d = 0;
		for (Iterator<Integer> it = list.iterator(); it.hasNext();) {
			result[d] = it.next().intValue();
			d++;
		}
		return result;
	}

	/**
	 * 将字符串按指定字符分割。
	 * 
	 * <p>
	 * 分隔符不会出现在目标数组中，连续的分隔符就被看作一个。如果字符串为<code>null</code>，则返回<code>null</code>。
	 * 
	 * <pre>
	 * StringUtil.split(null, *)                = null
	 * StringUtil.split(&quot;&quot;, *)                  = []
	 * StringUtil.split(&quot;abc def&quot;, null)        = [&quot;abc&quot;, &quot;def&quot;]
	 * StringUtil.split(&quot;abc def&quot;, &quot; &quot;)         = [&quot;abc&quot;, &quot;def&quot;]
	 * StringUtil.split(&quot;abc  def&quot;, &quot; &quot;)        = [&quot;abc&quot;, &quot;def&quot;]
	 * StringUtil.split(&quot; ab:  cd::ef  &quot;, &quot;:&quot;)  = [&quot;ab&quot;, &quot;cd&quot;, &quot;ef&quot;]
	 * StringUtil.split(&quot;abc.def&quot;, &quot;&quot;)          = [&quot;abc.def&quot;]
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param str
	 *            要分割的字符串
	 * @param separatorChars
	 *            分隔符
	 * 
	 * @return 分割后的字符串数组，如果原字符串为<code>null</code>，则返回<code>null</code>
	 */
	public static String[] split(String str, String separatorChars) {
		return split(str, separatorChars, -1);
	}

	/**
	 * 将字符串按指定字符分割。
	 * 
	 * <p>
	 * 分隔符不会出现在目标数组中，连续的分隔符就被看作一个。如果字符串为<code>null</code>，则返回<code>null</code>。
	 * 
	 * <pre>
	 * StringUtil.split(null, *, *)                 = null
	 * StringUtil.split(&quot;&quot;, *, *)                   = []
	 * StringUtil.split(&quot;ab cd ef&quot;, null, 0)        = [&quot;ab&quot;, &quot;cd&quot;, &quot;ef&quot;]
	 * StringUtil.split(&quot;  ab   cd ef  &quot;, null, 0)  = [&quot;ab&quot;, &quot;cd&quot;, &quot;ef&quot;]
	 * StringUtil.split(&quot;ab:cd::ef&quot;, &quot;:&quot;, 0)        = [&quot;ab&quot;, &quot;cd&quot;, &quot;ef&quot;]
	 * StringUtil.split(&quot;ab:cd:ef&quot;, &quot;:&quot;, 2)         = [&quot;ab&quot;, &quot;cdef&quot;]
	 * StringUtil.split(&quot;abc.def&quot;, &quot;&quot;, 2)           = [&quot;abc.def&quot;]
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param str
	 *            要分割的字符串
	 * @param separatorChars
	 *            分隔符
	 * @param max
	 *            返回的数组的最大个数，如果小于等于0，则表示无限制
	 * 
	 * @return 分割后的字符串数组，如果原字符串为<code>null</code>，则返回<code>null</code>
	 */
	public static String[] split(String str, String separatorChars, int max) {
		if (str == null) {
			return null;
		}

		int length = str.length();

		if (length == 0) {
			return ArrayKit.EMPTY_STRING_ARRAY;
		}

		List<String> list = new ArrayList<String>();
		int sizePlus1 = 1;
		int i = 0;
		int start = 0;
		boolean match = false;

		if (separatorChars == null) {
			// null表示使用空白作为分隔符
			while (i < length) {
				if (Character.isWhitespace(str.charAt(i))) {
					if (match) {
						if (sizePlus1++ == max) {
							i = length;
						}

						list.add(str.substring(start, i));
						match = false;
					}

					start = ++i;
					continue;
				}

				match = true;
				i++;
			}
		} else if (separatorChars.length() == 1) {
			// 优化分隔符长度为1的情形
			char sep = separatorChars.charAt(0);

			while (i < length) {
				if (str.charAt(i) == sep) {
					if (match) {
						if (sizePlus1++ == max) {
							i = length;
						}

						list.add(str.substring(start, i));
						match = false;
					}

					start = ++i;
					continue;
				}

				match = true;
				i++;
			}
		} else {
			// 一般情形
			while (i < length) {
				if (separatorChars.indexOf(str.charAt(i)) >= 0) {
					if (match) {
						if (sizePlus1++ == max) {
							i = length;
						}

						list.add(str.substring(start, i));
						match = false;
					}

					start = ++i;
					continue;
				}

				match = true;
				i++;
			}
		}

		if (match) {
			list.add(str.substring(start, i));
		}

		return (String[]) list.toArray(new String[list.size()]);
	}

	/**
	 * 将字符串按指定字符分割。
	 * 
	 * <p>
	 * 分隔符不会出现在目标数组中，连续的分隔符就被看作一个。如果字符串为<code>null</code>，则返回<code>[]</code>。
	 * 
	 * <pre>
	 *    StringUtil.split(null, *)         = []
	 *    StringUtil.split(&quot;&quot;, *)           = []
	 *    StringUtil.split(&quot;a.b.c&quot;, '.')    = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]
	 *    StringUtil.split(&quot;a..b.c&quot;, '.')   = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]
	 *    StringUtil.split(&quot;a:b:c&quot;, '.')    = [&quot;a:b:c&quot;]
	 *    StringUtil.split(&quot;a b c&quot;, ' ')    = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]
	 * </pre>
	 * 
	 * </p>
	 * 
	 * @param str
	 *            要分割的字符串
	 * @param separatorChar
	 *            分隔符
	 * 
	 * @return 分割后的字符串数组，如果原字符串为<code>null</code>，则返回<code>[]</code>
	 */
	public static String[] split(String str, char separateChar) {
		if (str == null) {
			return ArrayKit.EMPTY_STRING_ARRAY;
		}
		int len = str.length();
		if (len == 0) {
			return ArrayKit.EMPTY_STRING_ARRAY;
		}
		List<String> list = new ArrayList<String>();
		int start = 0;
		boolean match = false;
		for (int i = 0; i < len; i++) {
			if (str.charAt(i) == separateChar) {
				if (match) {
					list.add(str.substring(start, i));
					match = false;
				}
				start = i + 1;
			} else {
				match = true;
			}
		}
		if (match) {
			list.add(str.substring(start));
		}
		return (String[]) list.toArray(new String[list.size()]);
	}

	public static String[] split(String str, CharacterMatch mt) {
		if (str == null) {
			return ArrayKit.EMPTY_STRING_ARRAY;
		}
		int len = str.length();
		if (len == 0) {
			return ArrayKit.EMPTY_STRING_ARRAY;
		}
		List<String> list = new ArrayList<String>();
		int start = 0;
		boolean match = false;
		for (int i = 0; i < len; i++) {
			if (mt.match(str.charAt(i))) {
				if (match) {
					list.add(str.substring(start, i));
					match = false;
				}
				start = i + 1;
			} else {
				match = true;
			}
		}
		if (match) {
			list.add(str.substring(start));
		}
		return (String[]) list.toArray(new String[list.size()]);
	}

	public static String removeAll(String src, char[] del) {
		if (src == null || src.length() == 0 || null == del || del.length == 0) {
			return src;
		}
		int srcLen = src.length();
		StringBuilder buffer = new StringBuilder(srcLen);
		int delLen = del.length;
		outer: for (int i = 0, n = srcLen; i < n; i++) {
			char check = src.charAt(i);
			for (int j = 0; j < delLen; j++) {
				if (check == del[j]) {
					continue outer;
				}
			}
			buffer.append(check);
		}
		return buffer.toString();
	}

	public static String removeAll(String src, String del) {
		if (src == null || src.length() == 0 || null == del
				|| del.length() == 0) {
			return src;
		}
		int strLen = src.length();
		StringBuilder builder = new StringBuilder(strLen);
		for (int i = 0; i < strLen; i++) {
			char ch = src.charAt(i);
			if (del.indexOf(ch) == -1) {
				builder.append(ch);
			}
		}
		return builder.toString();
	}

	// public static String remove(String str, String moveChars) {
	// if (str == null || str.length() == 0 || null == moveChars
	// || moveChars.length() == 0) {
	// return str;
	// }
	// int strLen = str.length();
	// StringBuilder builder = new StringBuilder(strLen);
	// for (int i = 0; i < strLen; i++) {
	// char ch = str.charAt(i);
	// if (moveChars.indexOf(ch) == -1) {
	// builder.append(ch);
	// }
	// }
	// return builder.toString();
	// }

	public static boolean contains(String str, char searchChar) {
		if (str == null || str.length() == 0) {
			return false;
		}
		return (str.indexOf(searchChar) >= 0);
	}

	/**
	 * get the page split by input compart
	 * 
	 * @param body
	 * @param compart
	 * @param page
	 * @return
	 */
	public static String getPage(String body, String compart, int page) {
		int start = 0;
		int end = 0;
		if (page <= 1) {
			end = body.indexOf(compart);
			if (end < 0) {
				return body;
			}
		} else {
			boolean reachEnd = false;
			for (int i = 0; i < page - 1; i++) {
				int index = body.indexOf(compart, start);
				if (index < 0) {
					reachEnd = true;
					break;
				}
				start = index + compart.length();
			}
			if (reachEnd) {
				end = body.length();
			} else {
				end = body.indexOf(compart, start);
				if (end < 0) {
					end = body.length();
				}
			}
		}
		return body.substring(start, end);
	}

	/**
	 * null safe
	 * 
	 * null --> false,"" -->true;
	 * 
	 * @param in
	 * @return
	 */
	public static boolean isEmptyLetterOrDigit(String in) {
		if (null == in)
			return false;
		if (0 == in.length())
			return true;
		for (int i = 0; i < in.length(); i++) {
			if (!Character.isLetterOrDigit(in.charAt(i))) {
				return false;
			}
		}
		return true;
	}

	/**
	 * null safe
	 * 
	 * null --> false; "" --> true
	 * 
	 * @param in
	 * @return
	 */
	public static boolean isEmptyOrEnglishLetterOrDigit(String in) {
		if (null == in)
			return false;
		if (0 == in.length())
			return true;
		return isEnglishLetterOrDigit(in);
	}

	/**
	 * null safe
	 * 
	 * null, "" --> false;
	 * 
	 * @param in
	 * @return
	 */
	public static boolean isEnglishLetterOrDigit(String in) {
		if (null == in || 0 == in.length())
			return false;
		for (int i = 0; i < in.length(); i++) {
			char now = in.charAt(i);
			// 0 :48 ~ 9 :57
			// A :65 ~ Z :90
			// a :97 ~ z :122
			if (now < '0' || now > 'z' || now > '9' && now < 'A' || now > 'Z'
					&& now < 'a') {
				return false;
			}
		}
		return true;
	}

	public static boolean isEnglishLetterOrDigit(char now) {
		if (now < '0' || now > 'z' || now > '9' && now < 'A' || now > 'Z'
				&& now < 'a') {
			return false;
		}
		return true;
	}

	/**
	 * null safe
	 * 
	 * null --> false, "" --> true;
	 * 
	 * @param in
	 * @return
	 */
	public static boolean isEmptyOrLetter(String in) {
		if (null == in)
			return false;
		if (0 == in.length())
			return true;
		for (int i = 0; i < in.length(); i++) {
			if (!Character.isLetter(in.charAt(i))) {
				return false;
			}
		}
		return true;
	}

	/**
	 * null safe
	 * 
	 * null --> false, "" and digits --> true;
	 * 
	 * @param in
	 * @return
	 */
	public static boolean isEmptyOrDigit(String in) {
		if (null == in)
			return false;
		if (0 == in.length())
			return true;
		return isDigit(in);
	}

	/**
	 * null safe
	 * 
	 * null , "" --> false
	 * 
	 * @param in
	 * @return
	 */
	public static boolean isDigit(String in) {
		if (null == in || 0 == in.length())
			return false;
		for (int i = 0; i < in.length(); i++) {
			if (!Character.isDigit(in.charAt(i))) {
				return false;
			}
		}
		return true;
	}

	/**
	 * get string simply convert the byte to char for each
	 * 
	 * @param bytes
	 * @return
	 */
	public static String getNoEncodingString(byte[] bytes) {
		final StringBuilder sb = new StringBuilder(bytes.length);
		for (byte item : bytes)
			sb.append((char) item);
		return sb.toString();
	}

	/**
	 * Helper method that adds items supplied by an iterator to a string buffer,
	 * separated by a given separator.
	 * 
	 * @param buffer
	 *            The buffer to which strings are to be appended.
	 * @param itt
	 *            The iterator providing items.
	 * @param sep
	 *            The string separating items.
	 * @param suffix
	 *            The string suffix items.
	 */

	public static void buildIterator(StringBuilder buffer, Iterator<String> itt,
			String sep, String suffix) {
		while (itt.hasNext()) {
			String item = (String) itt.next();
			buffer.append(item);
			if (itt.hasNext()) {
				buffer.append(sep);
			}
			buffer.append(suffix);
		}
	}

}